﻿// ===============================================================================
// ParamValidationAspect.cs
// EOS Framework
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Tools.Aspects
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Reflection;
    using PostSharp.Laos;

    /// <summary>
    /// Base class for all aspects for validating method parameters. Validates the parameter 
    /// with the specified Parametername.
    /// </summary>
    [Serializable]
    [AttributeUsage(AttributeTargets.Method)]
    [SuppressMessage("Microsoft.Design", "CA1044")]
    public abstract class ParamValidationAspect : OnMethodBoundaryAspect
    {
        /// <summary>
        /// The name of the param to validate.
        /// </summary>
        private string paramName;

        /// <summary>
        /// Gets or sets the name of the parameter.
        /// </summary>
        /// <value>The name of the parameter.</value>
        /// <exception cref="ArgumentNullException"><param name="paramName"/> is null (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentException"><paramref name="paramName"/> is a string of length 0
        /// or contains only blanks.</exception>
        public string ParamName
        {
            get { return paramName; }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }

                if (StringHelper.IsEmptyOrBlanksOnly(value))
                {
                    throw new ArgumentException(
                        "String parameter cannot be empty and cannot contain only blanks", "value");
                }
                
                paramName = value;
            }
        }
  
        /// <summary>
        /// Initializes a new instance of the <see cref="ParamValidationAspect"/> class
        /// with the name of the parameter to validate.
        /// </summary>
        /// <param name="name">The name of the parameter to validate. Cannot be null (Nothing in Visual Basic)
        /// or empty or have only blanks.</param>
        /// <exception cref="ArgumentNullException"><param name="paramName"/> is null (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentException"><paramref name="paramName"/> is a string of length 0
        /// or contains only blanks.</exception>
        protected ParamValidationAspect(string paramName)
        {
            if (paramName == null)
            {
                throw new ArgumentNullException("paramName");
            }

            if (StringHelper.IsEmptyOrBlanksOnly(paramName))
            {
                throw new ArgumentException(
                    "String parameter cannot be empty and cannot contain only blanks", "paramName");
            }

            this.paramName = paramName;
        }

        /// <summary>
        /// Method executed <b>before</b> the body of methods to which this aspect is applied.
        /// </summary>
        /// <param name="eventArgs">Event arguments specifying which method
        /// is being executed, which are its arguments, and how should the execution continue
        /// after the execution of <see cref="M:PostSharp.Laos.IOnMethodBoundaryAspect.OnEntry(PostSharp.Laos.MethodExecutionEventArgs)"/>.</param>
        /// <remarks>
        /// If the aspect is applied to a constructor, the current method is invoked
        /// after the <b>this</b> pointer has been initialized, that is, after
        /// the base constructor has been called.
        /// </remarks>
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            ParameterInfo[] pinfo = eventArgs.Method.GetParameters();
            foreach (ParameterInfo info in pinfo)
            {
                if (info.Name == ParamName)
                {
                    OnValidate(eventArgs.GetReadOnlyArgumentArray()[info.Position]);
                }
            }
            base.OnEntry(eventArgs);
        }

        /// <summary>
        /// Called when when the value with the specified name passed to the current method
        /// must be validated.
        /// </summary>
        /// <param name="value">The value to valide.</param>
        /// <remarks>When implementing this method, throw an exception if the passed value
        /// is not valid.</remarks>
        protected abstract void OnValidate(object value);
    }
}
